package ar.uade.edu.fain.ad.server.service;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import ar.uade.edu.fain.ad.common.exception.FacturaYaPagadaException;
import ar.uade.edu.fain.ad.common.exception.PedidoFacturadoException;
import ar.uade.edu.fain.ad.common.exception.PedidoPendienteException;
import ar.uade.edu.fain.ad.common.remote.CajaServicioRemoto;
import ar.uade.edu.fain.ad.common.transfer.CajaVO;
import ar.uade.edu.fain.ad.common.transfer.CierreCajaVO;
import ar.uade.edu.fain.ad.common.transfer.FacturaVO;
import ar.uade.edu.fain.ad.common.transfer.LocalVO;
import ar.uade.edu.fain.ad.common.transfer.MedioPagoVO;
import ar.uade.edu.fain.ad.common.transfer.PedidoVO;
import ar.uade.edu.fain.ad.server.business.Caja;
import ar.uade.edu.fain.ad.server.business.CierreCaja;
import ar.uade.edu.fain.ad.server.business.Comision;
import ar.uade.edu.fain.ad.server.business.Factura;
import ar.uade.edu.fain.ad.server.business.MedioPago;
import ar.uade.edu.fain.ad.server.business.Pedido;
import ar.uade.edu.fain.ad.server.dao.CajaDAO;
import ar.uade.edu.fain.ad.server.dao.CierreCajaDAO;
import ar.uade.edu.fain.ad.server.dao.ComisionDAO;
import ar.uade.edu.fain.ad.server.dao.FacturaDAO;
import ar.uade.edu.fain.ad.server.dao.MedioPagoDAO;
import ar.uade.edu.fain.ad.server.dao.PedidoDAO;
import ar.uade.edu.fain.ad.server.util.persistence.TransactionManager;

public class CajaSRV extends UnicastRemoteObject implements CajaServicioRemoto {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private TransactionManager transactionManager;

	private PedidoDAO pedidoDAO;

	private CajaDAO cajaDAO;
	
	private FacturaDAO facturaDAO;
	
	private MedioPagoDAO medioPagoDAO;
	
	private ComisionDAO comisionDAO;
	
	private CierreCajaDAO cierreCajaDAO; 
	
	public CajaSRV() throws RemoteException {
		super();
	}

	public TransactionManager getTransactionManager() {
		return transactionManager;
	}

	public void setTransactionManager(TransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	public MedioPagoDAO getMedioPagoDAO() {
		return medioPagoDAO;
	}

	public void setMedioPagoDAO(MedioPagoDAO medioPagoDAO) {
		this.medioPagoDAO = medioPagoDAO;
	}

	public FacturaDAO getFacturaDAO() {
		return facturaDAO;
	}

	public void setFacturaDAO(FacturaDAO facturaDAO) {
		this.facturaDAO = facturaDAO;
	}

	public CajaDAO getCajaDAO() {
		return cajaDAO;
	}

	public void setCajaDAO(CajaDAO cajaDAO) {
		this.cajaDAO = cajaDAO;
	}

	public PedidoDAO getPedidoDAO() {
		return pedidoDAO;
	}

	public void setPedidoDAO(PedidoDAO pedidoDAO) {
		this.pedidoDAO = pedidoDAO;
	}

	public ComisionDAO getComisionDAO() {
		return comisionDAO;
	}

	public void setComisionDAO(ComisionDAO comisionDAO) {
		this.comisionDAO = comisionDAO;
	}

	public CierreCajaDAO getCierreCajaDAO() {
		return cierreCajaDAO;
	}

	public void setCierreCajaDAO(CierreCajaDAO cierreCajaDAO) {
		this.cierreCajaDAO = cierreCajaDAO;
	}
	
	@Override
	public List<MedioPagoVO> buscarMediosPagoHabilitados()
			throws RemoteException {
		ArrayList<MedioPagoVO> vos;
		try {
			getTransactionManager().start();
			final List<MedioPago> mediosPago = getMedioPagoDAO().getAll();
			vos = new ArrayList<MedioPagoVO>();
			for (MedioPago medioPago : mediosPago) {
				vos.add(medioPago.toVO());
			}
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return vos;
	}
	
	@Override
	public List<PedidoVO> buscarPedidosSinFactura(final LocalVO localVO)
			throws RemoteException {
		ArrayList<PedidoVO> vos;
		try {
			getTransactionManager().start();
			final List<Pedido> pedidos = getPedidoDAO().buscarPedidosSinFactura(
					localVO.getId());
			vos = new ArrayList<PedidoVO>();
			for (Pedido pedido : pedidos) {
				vos.add(pedido.toVO());
			}
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return vos;
	}

	@Override
	public FacturaVO facturar(
			final CajaVO cajaVO, final Set<PedidoVO> pedidoVOs) 
			throws RemoteException, PedidoFacturadoException {
		FacturaVO vo;
		try {
			getTransactionManager().start();
			if (pedidoVOs == null || pedidoVOs.isEmpty()) {
				throw new IllegalArgumentException(
						"Se necesitan pedidos para poder facturar.");
			}
			final Caja caja = getCajaDAO().get(cajaVO.getId());
			final HashSet<Pedido> pedidos = new HashSet<Pedido>();
			for (PedidoVO pedidoVO : pedidoVOs) {
				final Pedido pedido = getPedidoDAO().get(pedidoVO.getId());
				if (pedido == null) {
					throw new IllegalArgumentException(
							"Pedido inexistente: " + pedidoVO.getId());
				}
				pedidos.add(pedido);
			}
			final Factura factura = caja.facturar(pedidos);
			getFacturaDAO().save(factura);
			getCajaDAO().update(caja);
			getTransactionManager().commit();
			vo = factura.toVO();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		} catch (PedidoFacturadoException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return vo;
	}

	@Override
	public void registrarPago(
			final FacturaVO facturaVO, final MedioPagoVO medioPagoVO)
			throws RemoteException, FacturaYaPagadaException, 
					IllegalArgumentException {
		try {
			getTransactionManager().start();
			final Factura factura = getFacturaDAO().get(facturaVO.getId());
			if (factura == null) {
				throw new IllegalArgumentException(
						"Factura inexistente: " + facturaVO.getId());
			}
			final MedioPago medioPago = getMedioPagoDAO().get(medioPagoVO.getId());
			if (medioPago == null) {
				throw new IllegalArgumentException(
						"Medio de pago inexistente: " + medioPagoVO.getId());
			}
			factura.pagar(medioPago);
			getFacturaDAO().update(factura);
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		} catch (FacturaYaPagadaException e) {
			getTransactionManager().rollback();
			throw e;
		}
	}

	@Override
	public void calcularComisiones(
			final LocalVO localVO, final Date fecha)
			throws PedidoPendienteException, RemoteException {
		try {
			getTransactionManager().start();
			final Long localId = localVO.getId();
			chequearEstadoLocal(localVO);
			final Caja caja = getCajaDAO().buscarParaLocal(localId);
			final Set<Factura> facturas = 
					getFacturaDAO().buscarFacturas(caja.getId(), fecha);
			final Set<Comision> comisiones = 
					caja.calcularComisionesSobre(facturas);
			for (Comision comision : comisiones) {
				getComisionDAO().save(comision);
			}
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
	}

	private void chequearEstadoLocal(final LocalVO localVO)
			throws RemoteException, PedidoPendienteException {
		final List<PedidoVO> pedidosPendientes = 
				buscarPedidosSinFactura(localVO);
		if (!pedidosPendientes.isEmpty()) {
			throw new PedidoPendienteException(pedidosPendientes);
		}
	}

	@Override
	public CierreCajaVO prepararCierre(final CajaVO cajaVO) 
			throws RemoteException, PedidoPendienteException {
		CierreCajaVO cierreCajaVO;
		try {
			getTransactionManager().start();
			chequearEstadoLocalDe(cajaVO);
			final Date fecha = new Date();
			final Double totalEfectivo = 
					getFacturaDAO().sumarTotalCon(
							cajaVO.getId(), MedioPago.TIPO_EFECTIVO, fecha);
			final Double totalCupones = 
					getFacturaDAO().sumarTotalCon(
							cajaVO.getId(), MedioPago.TIPO_TARJETA, fecha);
			cierreCajaVO = new CierreCajaVO();
			cierreCajaVO.setCajaVO(cajaVO);
			cierreCajaVO.setTotalCuponesEnSistema(totalCupones);
			cierreCajaVO.setTotalEfectivoEnSistema(totalEfectivo);
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return cierreCajaVO;
	}

	private void chequearEstadoLocalDe(final CajaVO cajaVO)
			throws RemoteException, PedidoPendienteException {
		final Caja caja = getCajaDAO().get(cajaVO.getId());
		final LocalVO localVO = new LocalVO();
		localVO.setId(caja.getLocal().getId());
		chequearEstadoLocal(localVO);
	}

	@Override
	public void confirmarCierre(final CierreCajaVO cierreCajaVO)
			throws RemoteException, PedidoPendienteException {
		try {
			getTransactionManager().start();
			//chequearEstadoLocalDe(cierreCajaVO.getCajaVO());
			final Caja caja = getCajaDAO().get(cierreCajaVO.getCajaVO().getId());
			final CierreCaja cierreCaja = caja.cerrar(cierreCajaVO);
			getCierreCajaDAO().save(cierreCaja);
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
	}


}
